TypeScriptã®'infer'ããŒã¯ãŒããæ·±ãæãäžããæ¡ä»¶ä»ãåã«ãããé«åºŠãªäœ¿çšæ³ãæ¢æ±ããŸãã匷åãªåæäœãšã³ãŒãã®æç¢ºæ§åäžã«åœ¹ç«ã¡ãŸãã
æ¡ä»¶ä»ãåæšè«ïŒTypeScriptã®'infer'ããŒã¯ãŒãããã¹ã¿ãŒãã
TypeScriptã®åã·ã¹ãã ã¯ãå
ç¢ã§ä¿å®ããããã³ãŒããäœæããããã®åŒ·åãªããŒã«ãæäŸããŸãããããã®ããŒã«ã®äžã§ããæ¡ä»¶ä»ãåã¯è€éãªåé¢ä¿ã衚çŸããããã®å€çšéãªã¡ã«ããºã ãšããŠéç«ã£ãŠããŸããç¹ã«inferããŒã¯ãŒãã¯ãæ¡ä»¶ä»ãåå
ã§é«åºŠãªå¯èœæ§ãè§£ãæŸã¡ãæŽç·Žãããåã®æœåºãšæäœãå¯èœã«ããŸãããã®å
æ¬çãªã¬ã€ãã§ã¯ãinferã®è€éããæ¢æ±ãããã®äœ¿çšæ³ãç¿åŸããã®ã«åœ¹ç«ã€å®è·µçãªäŸãšæŽå¯ãæäŸããŸãã
æ¡ä»¶ä»ãåãçè§£ãã
inferã«å
¥ãåã«ãæ¡ä»¶ä»ãåã®åºæ¬ãçè§£ããããšãéèŠã§ããæ¡ä»¶ä»ãåã䜿çšãããšãJavaScriptã®äžé
æŒç®åãšåæ§ã«ãæ¡ä»¶ã«äŸåããåãå®çŸ©ã§ããŸããæ§æã¯æ¬¡ã®ãã¿ãŒã³ã«åŸããŸãã
T extends U ? X : Y
ããã§ãåTãåUã«ä»£å
¥å¯èœã§ããå Žåãçµæã®åã¯Xã«ãªããããã§ãªãå Žåã¯Yã«ãªããŸãã
äŸ:
type IsString<T> = T extends string ? true : false;
type StringCheck = IsString<string>; // type StringCheck = true
type NumberCheck = IsString<number>; // type NumberCheck = false
ãã®ç°¡åãªäŸã¯ãæ¡ä»¶ä»ãåã䜿çšããŠãåãæååã§ãããã©ããã倿ããæ¹æ³ã瀺ããŠããŸãããã®æŠå¿µã¯ããè€éãªã·ããªãªã«ãæ¡åŒµãããinferããŒã¯ãŒããžã®éãéããŸãã
'infer'ããŒã¯ãŒãã®å°å ¥
inferããŒã¯ãŒãã¯ãæ¡ä»¶ä»ãåã®trueãã©ã³ãå
ã§äœ¿çšããããã§ãã¯ãããŠããåããæšè«ã§ããå倿°ãå°å
¥ããŸããããã«ãããåã®ç¹å®ã®éšåãæœåºããçµæã®åã§äœ¿çšããããšãã§ããŸãã
æ§æ:
T extends (infer R) ? X : Y
ãã®æ§æã§ã¯ãRã¯Tã®æ§é ããæšè«ãããå倿°ã§ããTããã¿ãŒã³ã«äžèŽããå ŽåãRã¯æšè«ãããåãä¿æããçµæã®åã¯Xã«ãªããŸããããã§ãªãå Žåã¯Yã«ãªããŸãã
'infer'ã®åºæ¬çãªäœ¿çšäŸ
1. 颿°ã®æ»ãå€ã®åãæšè«ãã
ãããããŠãŒã¹ã±ãŒã¹ã¯ã颿°ã®æ»ãå€ã®åãæšè«ããããšã§ããããã¯ãæ¬¡ã®æ¡ä»¶ä»ãåã§å®çŸã§ããŸãã
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
説æ:
T extends (...args: any) => any: ãã®å¶çŽã¯ãTã颿°ã§ããããšãä¿èšŒããŸãã(...args: any) => infer R: ãã®ãã¿ãŒã³ã¯é¢æ°ã«äžèŽããæ»ãå€ã®åãRãšããŠæšè«ããŸããR : any:Tã颿°ã§ã¯ãªãå Žåãçµæã®åã¯anyã«ãªããŸãã
äŸ:
function greet(name: string): string {
return `Hello, ${name}!`;
}
type GreetingReturnType = ReturnType<typeof greet>; // type GreetingReturnType = string
function calculate(a: number, b: number): number {
return a + b;
}
type CalculateReturnType = ReturnType<typeof calculate>; // type CalculateReturnType = number
ãã®äŸã¯ãReturnTypeãgreet颿°ãšcalculate颿°ã®æ»ãå€ã®åãæ£åžžã«æœåºããæ¹æ³ã瀺ããŠããŸãã
2. é åèŠçŽ ã®åãæšè«ãã
ãã1ã€ã®é »ç¹ãªãŠãŒã¹ã±ãŒã¹ã¯ãé åã®èŠçŽ åãæœåºããããšã§ãã
type ElementType<T> = T extends (infer U)[] ? U : never;
説æ:
T extends (infer U)[]: ãã®ãã¿ãŒã³ã¯é åã«äžèŽããèŠçŽ ã®åãUãšããŠæšè«ããŸããU : never:Tãé åã§ã¯ãªãå Žåãçµæã®åã¯neverã«ãªããŸãã
äŸ:
type StringArrayElement = ElementType<string[]>; // type StringArrayElement = string
type NumberArrayElement = ElementType<number[]>; // type NumberArrayElement = number
type MixedArrayElement = ElementType<(string | number)[]>; // type MixedArrayElement = string | number
type NotAnArray = ElementType<number>; // type NotAnArray = never
ããã¯ãElementTypeãããŸããŸãªé
ååã®èŠçŽ åãæ£ããæšè«ããæ¹æ³ã瀺ããŠããŸãã
'infer'ã®é«åºŠãªäœ¿çšæ³
1. 颿°ã®ãã©ã¡ãŒã¿ãæšè«ãã
æ»ãå€ã®åãæšè«ããã®ãšåæ§ã«ãinferãšã¿ãã«ã䜿çšããŠé¢æ°ã®ãã©ã¡ãŒã¿ãæšè«ã§ããŸãã
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
説æ:
T extends (...args: any) => any: ãã®å¶çŽã¯ãTã颿°ã§ããããšãä¿èšŒããŸãã(...args: infer P) => any: ãã®ãã¿ãŒã³ã¯é¢æ°ã«äžèŽãããã©ã¡ãŒã¿ã®åãã¿ãã«PãšããŠæšè«ããŸããP : never:Tã颿°ã§ã¯ãªãå Žåãçµæã®åã¯neverã«ãªããŸãã
äŸ:
function logMessage(message: string, level: 'info' | 'warn' | 'error'): void {
console.log(`[${level.toUpperCase()}] ${message}`);
}
type LogMessageParams = Parameters<typeof logMessage>; // type LogMessageParams = [message: string, level: "info" | "warn" | "error"]
function processData(data: any[], callback: (item: any) => void): void {
data.forEach(callback);
}
type ProcessDataParams = Parameters<typeof processData>; // type ProcessDataParams = [data: any[], callback: (item: any) => void]
Parametersã¯ã颿°ã®åŒæ°ã®é åºãšåãä¿æããªããããã©ã¡ãŒã¿ã®åãã¿ãã«ãšããŠæœåºããŸãã
2. ãªããžã§ã¯ãåããã®ããããã£ã®æœåº
inferã¯ããªããžã§ã¯ãåããç¹å®ã®ããããã£ãæœåºããããã«ã䜿çšã§ããŸããããã¯ããè€éãªæ¡ä»¶ä»ãåãå¿
èŠãšããŸããã匷åãªåæäœãå¯èœã«ããŸãã
type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
説æ:
K in keyof T: ããã¯ãåTã®ãã¹ãŠã®ããŒãå埩åŠçããŸããT[K] extends U ? K : never: ãã®æ¡ä»¶ä»ãåã¯ãããŒKã«ããããããã£ã®åïŒã€ãŸãT[K]ïŒãåUã«ä»£å ¥å¯èœã§ãããã©ããããã§ãã¯ããŸããä»£å ¥å¯èœã§ããã°ãããŒKã¯çµæã®åã«å«ãŸããããã§ãªããã°neverã䜿çšããŠé€å€ãããŸãã- ãã®å
šäœçãªæ§æã¯ãåã
Uãæ¡åŒµããããããã£ã®ã¿ãæã€æ°ãããªããžã§ã¯ãåãäœæããŸãã
äŸ:
interface Person {
name: string;
age: number;
city: string;
country: string;
}
type StringProperties = PickByType<Person, string>; // type StringProperties = { name: string; city: string; country: string; }
type NumberProperties = PickByType<Person, number>; // type NumberProperties = { age: number; }
PickByTypeã䜿çšãããšãæ¢åã®åããç¹å®ã®åã®ããããã£ã®ã¿ãå«ãæ°ããåãäœæã§ããŸãã
3. ãã¹ããããåã®æšè«
inferã¯ãæ·±ããã¹ããããæ§é ããåãæœåºããããã«é£éããã³ãã¹ãã§ããŸããäŸãã°ããã¹ããããé
åã®æãå
åŽã®èŠçŽ ã®åãæœåºããããšãèããŠã¿ãŸãããã
type DeepArrayElement<T> = T extends (infer U)[] ? DeepArrayElement<U> : T;
説æ:
T extends (infer U)[]: ããã¯ãTãé åã§ãããã©ããããã§ãã¯ããèŠçŽ ã®åãUãšããŠæšè«ããŸããDeepArrayElement<U>:Tãé åã®å Žåãåã¯èŠçŽ åUã§DeepArrayElementãååž°çã«åŒã³åºããŸããT:Tãé åã§ã¯ãªãå Žåãåã¯Tèªäœãè¿ããŸãã
äŸ:
type NestedStringArray = string[][][];
type DeepString = DeepArrayElement<NestedStringArray>; // type DeepString = string
type MixedNestedArray = (number | string)[][][][];
type DeepMixed = DeepArrayElement<MixedNestedArray>; // type DeepMixed = string | number
type RegularNumber = DeepArrayElement<number>; // type RegularNumber = number
ãã®ååž°çãªã¢ãããŒãã«ãããé åã®æãæ·±ããã¹ãã¬ãã«ã«ããèŠçŽ ã®åãæœåºã§ããŸãã
å®äžçã§ã®ã¢ããªã±ãŒã·ã§ã³
inferããŒã¯ãŒãã¯ãåçãªåæäœãå¿
èŠãšãããããŸããŸãªã·ããªãªã§å¿çšãããŠããŸãã以äžã«ããã€ãã®å®è·µçãªäŸã瀺ããŸãã
1. åå®å šãªã€ãã³ããšããã¿ãŒã®äœæ
inferã䜿çšããŠãã€ãã³ããã³ãã©ãæ£ããããŒã¿åãåãåãããšãä¿èšŒããåå®å
šãªã€ãã³ããšããã¿ãŒãäœæã§ããŸãã
type EventMap = {
'data': { value: string };
'error': { message: string };
};
type EventName<T extends EventMap> = keyof T;
type EventData<T extends EventMap, K extends EventName<T>> = T[K];
type EventHandler<T extends EventMap, K extends EventName<T>> = (data: EventData<T, K>) => void;
class EventEmitter<T extends EventMap> {
private listeners: { [K in EventName<T>]?: EventHandler<T, K>[] } = {};
on<K extends EventName<T>>(event: K, handler: EventHandler<T, K>): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(handler);
}
emit<K extends EventName<T>>(event: K, data: EventData<T, K>): void {
this.listeners[event]?.forEach(handler => handler(data));
}
}
const emitter = new EventEmitter<EventMap>();
emitter.on('data', (data) => {
console.log(`Received data: ${data.value}`);
});
emitter.on('error', (error) => {
console.error(`An error occurred: ${error.message}`);
});
emitter.emit('data', { value: 'Hello, world!' });
emitter.emit('error', { message: 'Something went wrong.' });
ãã®äŸã§ã¯ãEventDataã¯æ¡ä»¶ä»ãåãšinferã䜿çšããŠãç¹å®ã®ã€ãã³ãåã«é¢é£ä»ããããããŒã¿åãæœåºããã€ãã³ããã³ãã©ãæ£ããåã®ããŒã¿ãåãåãããšãä¿èšŒããŸãã
2. åå®å šãªãªãã¥ãŒãµãŒã®å®è£
inferãæŽ»çšããŠãç¶æ
管çã®ããã®åå®å
šãªãªãã¥ãŒãµãŒé¢æ°ãäœæã§ããŸãã
type Action<T extends string, P = undefined> = P extends undefined
? { type: T }
: { type: T; payload: P };
type Reducer<S, A extends Action<string>> = (state: S, action: A) => S;
// Example Actions
type IncrementAction = Action<'INCREMENT'>;
type DecrementAction = Action<'DECREMENT'>;
type SetValueAction = Action<'SET_VALUE', number>;
// Example State
interface CounterState {
value: number;
}
// Example Reducer
const counterReducer: Reducer<CounterState, IncrementAction | DecrementAction | SetValueAction> = (
state: CounterState,
action: IncrementAction | DecrementAction | SetValueAction
): CounterState => {
switch (action.type) {
case 'INCREMENT':
return { ...state, value: state.value + 1 };
case 'DECREMENT':
return { ...state, value: state.value - 1 };
case 'SET_VALUE':
return { ...state, value: action.payload };
default:
return state;
}
};
// Usage
const initialState: CounterState = { value: 0 };
const newState1 = counterReducer(initialState, { type: 'INCREMENT' }); // newState1.value is 1
const newState2 = counterReducer(newState1, { type: 'SET_VALUE', payload: 10 }); // newState2.value is 10
ãã®äŸã¯çŽæ¥inferã䜿çšããŠããŸããããããè€éãªãªãã¥ãŒãµãŒã·ããªãªã®åºç€ãç¯ããŸããinferã¯ãç°ãªãActionåããpayloadåãåçã«æœåºããããã«é©çšã§ãããªãã¥ãŒãµãŒé¢æ°å
ã§ããå³å¯ãªåãã§ãã¯ãå¯èœã«ããŸããããã¯ã倿°ã®ã¢ã¯ã·ã§ã³ãšè€éãªç¶æ
æ§é ãæã€å€§èŠæš¡ãªã¢ããªã±ãŒã·ã§ã³ã§ç¹ã«åœ¹ç«ã¡ãŸãã
3. APIã¬ã¹ãã³ã¹ããã®åçãªåçæ
APIãæ±ãéãinferã䜿çšããŠAPIã¬ã¹ãã³ã¹ã®æ§é ããTypeScriptåãèªåçã«çæã§ããŸããããã«ãããå€éšããŒã¿ãœãŒã¹ãšã®ããåãã«ãããåå®å
šæ§ãä¿èšŒãããŸãã
äžè¬çãªAPIã¬ã¹ãã³ã¹ããããŒã¿åãæœåºãããåçŽãªã·ããªãªãèããŠã¿ãŸãããã
type ApiResponse<T> = {
status: number;
data: T;
message?: string;
};
type ExtractDataType<T> = T extends ApiResponse<infer U> ? U : never;
// Example API Response
type User = {
id: number;
name: string;
email: string;
};
type UserApiResponse = ApiResponse<User>;
type ExtractedUser = ExtractDataType<UserApiResponse>; // type ExtractedUser = User
ExtractDataTypeã¯inferã䜿çšããŠApiResponse<U>ããåUãæœåºããAPIããè¿ãããããŒã¿æ§é ã«ã¢ã¯ã»ã¹ããããã®åå®å
šãªæ¹æ³ãæäŸããŸãã
ãã¹ããã©ã¯ãã£ã¹ãšèæ ®äºé
- æç¢ºããšå¯èªæ§: ã³ãŒãã®å¯èªæ§ãåäžãããããã«ãèšè¿°çãªå倿°åïŒäŸïŒ
Rã®ä»£ããã«ReturnTypeãªã©ïŒã䜿çšããŠãã ããã - ããã©ãŒãã³ã¹:
inferã¯åŒ·åã§ãããé床ãªäœ¿çšã¯åãã§ãã¯ã®ããã©ãŒãã³ã¹ã«åœ±é¿ãäžããå¯èœæ§ããããŸããç¹ã«å€§èŠæš¡ãªã³ãŒãããŒã¹ã§ã¯ãæ éã«äœ¿çšããŠãã ããã - ãšã©ãŒåŠç: åãæåŸ
ããããã¿ãŒã³ã«äžèŽããªãã±ãŒã¹ãåŠçããããã«ãæ¡ä»¶ä»ãåã®
falseãã©ã³ãã«ã¯åžžã«ãã©ãŒã«ããã¯åïŒäŸïŒanyãŸãã¯neverïŒãæäŸããŠãã ããã - è€éæ§: ãã¹ãããã
inferã¹ããŒãã¡ã³ããæã€é床ã«è€éãªæ¡ä»¶ä»ãåã¯ãçè§£ãšä¿å®ãå°é£ã«ãªãå¯èœæ§ãããããé¿ããŠãã ãããå¿ èŠã«å¿ããŠãã³ãŒããããå°ããããã管çããããåã«ãªãã¡ã¯ã¿ãªã³ã°ããŠãã ããã - ãã¹ã: ããŸããŸãªå ¥ååã§æ¡ä»¶ä»ãåã培åºçã«ãã¹ãããæåŸ ã©ããã«åäœããããšã確èªããŠãã ããã
ã°ããŒãã«ãªèæ ®äºé
TypeScriptãšinferãã°ããŒãã«ãªã³ã³ããã¹ãã§äœ¿çšããå Žåã以äžãèæ
®ããŠãã ããã
- ããŒã«ã©ã€ãŒãŒã·ã§ã³ãšåœéå (i18n): åã¯ãç°ãªããã±ãŒã«ãããŒã¿åœ¢åŒã«é©å¿ããå¿
èŠãããå ŽåããããŸããæ¡ä»¶ä»ãåãš
inferã䜿çšããŠããã±ãŒã«åºæã®èŠä»¶ã«åºã¥ããŠããŸããŸãªããŒã¿æ§é ãåçã«åŠçããŸããäŸãã°ãæ¥ä»ãé貚ã¯åœã«ãã£ãŠç°ãªã衚çŸã«ãªãããšããããŸãã - ã°ããŒãã«ãªãŠãŒã¶ãŒåãã®APIèšèš: ã°ããŒãã«ãªã¢ã¯ã»ã·ããªãã£ã念é ã«çœ®ããŠAPIãèšèšããŠãã ããããŠãŒã¶ãŒã®æåšå°ã«é¢ä¿ãªããçè§£ããããåŠçããããäžè²«ããããŒã¿æ§é ãšåœ¢åŒã䜿çšããŠãã ãããåå®çŸ©ã¯ããã®äžè²«æ§ãåæ ããå¿ èŠããããŸãã
- ã¿ã€ã ãŸãŒã³: æ¥ä»ãšæå»ãæ±ãéã«ã¯ãã¿ã€ã ãŸãŒã³ã®éãã«æ³šæããŠãã ãããé©åãªã©ã€ãã©ãªïŒäŸïŒLuxonãdate-fnsïŒã䜿çšããŠã¿ã€ã ãŸãŒã³å€æãåŠçããç°ãªãå°åéã§æ£ç¢ºãªããŒã¿è¡šçŸãä¿èšŒããŠãã ãããAPIã¬ã¹ãã³ã¹ã§ã¯ãæ¥ä»ãšæå»ãUTC圢åŒã§è¡šçŸããããšãæ€èšããŠãã ããã
- æåã®éã: ããŒã¿è¡šçŸãšè§£éã«ãããæåã®éãã«æ³šæããŠãã ãããäŸãã°ãååãäœæãé»è©±çªå·ã¯åœã«ãã£ãŠç°ãªã圢åŒãæã€ããšããããŸããåå®çŸ©ããããã®ããªãšãŒã·ã§ã³ã«å¯Ÿå¿ã§ããããšã確èªããŠãã ããã
- é貚ã®åŠç: ééç䟡å€ãæ±ãéã«ã¯ãäžè²«ããé貚衚çŸïŒäŸïŒISO 4217é貚ã³ãŒãïŒã䜿çšããé貚æç®ãé©åã«åŠçããŠãã ããã粟床ã«é¢ããåé¡ãé¿ããæ£ç¢ºãªèšç®ãä¿èšŒããããã«ãé貚æäœçšã«èšèšãããã©ã€ãã©ãªã䜿çšããŠãã ããã
äŸãã°ãç°ãªãå°åãããŠãŒã¶ãŒãããã¡ã€ã«ããã§ããããã·ããªãªãèããŠã¿ãŸãããããã®å Žåãäœæã®åœ¢åŒã¯åœã«ãã£ãŠç°ãªããŸããæ¡ä»¶ä»ãåãšinferã䜿çšãããšããŠãŒã¶ãŒã®æåšå°ã«åºã¥ããŠåå®çŸ©ãåçã«èª¿æŽã§ããŸãã
type AddressFormat<CountryCode extends string> = CountryCode extends 'US'
? { street: string; city: string; state: string; zipCode: string; }
: CountryCode extends 'CA'
? { street: string; city: string; province: string; postalCode: string; }
: { addressLines: string[]; city: string; country: string; };
type UserProfile<CountryCode extends string> = {
id: number;
name: string;
email: string;
address: AddressFormat<CountryCode>;
countryCode: CountryCode; // Add country code to profile
};
// Example Usage
type USUserProfile = UserProfile<'US'>; // Has US address format
type CAUserProfile = UserProfile<'CA'>; // Has Canadian address format
type GenericUserProfile = UserProfile<'DE'>; // Has Generic (international) address format
countryCodeãUserProfileåã«å«ãããã®ã³ãŒãã«åºã¥ããæ¡ä»¶ä»ãåã䜿çšããããšã§ãaddressåãåå°åã§æåŸ
ããã圢åŒã«åãããŠåçã«èª¿æŽã§ããŸããããã«ãããç°ãªãåœã
éã§å€æ§ãªããŒã¿åœ¢åŒãåå®å
šã«åŠçã§ããŸãã
çµè«
inferããŒã¯ãŒãã¯ãTypeScriptã®åã·ã¹ãã ãžã®åŒ·åãªè¿œå æ©èœã§ãããæ¡ä»¶ä»ãåå
ã§ã®æŽç·Žãããåæäœãšæœåºãå¯èœã«ããŸããinferãç¿åŸããããšã§ãããå
ç¢ã§åå®å
šãã€ä¿å®ããããã³ãŒããäœæã§ããŸãã颿°ã®æ»ãå€ã®åæšè«ããè€éãªãªããžã§ã¯ãããã®ããããã£æœåºãŸã§ããã®å¯èœæ§ã¯åºå€§ã§ããinferãæ
éã«äœ¿çšããã³ãŒããé·æçã«çè§£ããããä¿å®ããããããã«ãæç¢ºããšå¯èªæ§ãåªå
ããããšãå¿ããªãã§ãã ããã
ãã®ã¬ã€ãã§ã¯ãinferãšãã®ã¢ããªã±ãŒã·ã§ã³ã®å
æ¬çãªæŠèŠãæäŸããŸãããæäŸãããäŸã詊ãããã远å ã®ãŠãŒã¹ã±ãŒã¹ãæ¢æ±ãããããŠãinferãæŽ»çšããTypeScriptéçºã¯ãŒã¯ãããŒã匷åããŠãã ããã